package com.simple.car.loader.server.tools;

import com.google.gson.Gson;
import com.simple.car.loader.server.annotation.Function;
import com.simple.car.loader.server.annotation.Loader;
import com.simple.car.loader.server.distapch.AutoDispatch;
import com.simple.car.loader.server.entity.Apply;

import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.ServerSocket;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;

@Function
public class Server {

    public static int CONNECTED = 0x00;

    public static int RUNNING = 0x01;

    public static int CLOSED = 0x02;

    public static final int PORT = 18866;

    private ServerSocket serverSocket;

    @Loader
    private AutoDispatch autoDispatch;

    private boolean isClose;

    private Map<Socket, Integer> statue;

    private Gson gson = new Gson();

    public Server() throws IOException {
        serverSocket = new ServerSocket(PORT);
        statue = new HashMap<>();
    }

    public void listen() {
        while (!isClose) {
            try {
                Socket accept = serverSocket.accept();
                handle(accept);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public void handle(Socket socket) {
        statue.put(socket, CONNECTED);
        ThreadPool.getInstance().addTask(() -> {
            try {
                InputStream inputStream = socket.getInputStream();
                DataInputStream dataInputStream = new DataInputStream(inputStream);
                statue.put(socket, RUNNING);
                int anInt = dataInputStream.readInt();
                byte[] data = new byte[anInt];
                dataInputStream.readFully(data);
                Apply apply = fromByteArray(data);
                autoDispatch.dispatch(socket, apply);
                inputStream.read();
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                try {
                    statue.put(socket, CLOSED);
                    socket.shutdownInput();
                    socket.shutdownOutput();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    public void close() {
        isClose = true;
        if (serverSocket != null) {
            try {
                serverSocket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private Apply fromByteArray(byte[] data) {
        Apply apply = null;
        try (DataInputStream dataInputStream = new DataInputStream(new ByteArrayInputStream(data))) {
            byte[] bytes = null;
            int len = dataInputStream.readInt();
            bytes = new byte[len];
            dataInputStream.readFully(bytes);
            String res = new String(bytes, StandardCharsets.UTF_8);
            apply = new Apply();
            len = dataInputStream.readInt();
            if (len > 0){
                bytes = new byte[len];
                dataInputStream.readFully(bytes);
                String type = new String(bytes, StandardCharsets.UTF_8);
                apply.setType(type);
            }
            len = dataInputStream.readInt();
            bytes = new byte[len];
            dataInputStream.readFully(bytes);
            apply.setRes(res);
            apply.setData(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return apply;
    }
}
